package com.pz.gulimall.ware.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.pz.gulimall.common.exception.BizCodeEnum;
import com.pz.gulimall.common.exception.RRException;
import com.pz.gulimall.common.to.LockStockResultTo;
import com.pz.gulimall.common.to.SkuHasStockTo;
import com.pz.gulimall.common.to.WareStockLockTo;
import com.pz.gulimall.common.utils.R;
import com.pz.gulimall.ware.entity.PurchaseDetailEntity;
import com.pz.gulimall.ware.entity.resultMap.RemainStockResultMap;
import com.pz.gulimall.ware.entity.resultMap.WareMeta;
import com.pz.gulimall.ware.entity.resultMap.WareStockReleaseParam;
import com.pz.gulimall.ware.feign.ProductFeign;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pz.gulimall.common.utils.PageUtils;
import com.pz.gulimall.common.utils.Query;

import com.pz.gulimall.ware.dao.WareSkuDao;
import com.pz.gulimall.ware.entity.WareSkuEntity;
import com.pz.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import static com.pz.gulimall.ware.config.MQConfig.deadLetterRoutingKey;
import static com.pz.gulimall.ware.config.MQConfig.wareEventExchangeName;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Resource
    private ProductFeign productFeign;
    @Resource
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String skuId = (String) params.get("skuId");
        String wareId = (String) params.get("wareId");
        if(!StringUtils.isEmpty(skuId)){
            queryWrapper.eq("sku_id", skuId);
        }
        if(!StringUtils.isEmpty(wareId)){
            queryWrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    /**
     * 将采购成功的采购项 入库
     * @param detailEntity
     */
    @Transactional
    @Override
    public void addStock(PurchaseDetailEntity detailEntity) {
        //判断是否存在 该库存，存在就更新，不存在就添加
        List<WareSkuEntity> wareSkuEntities = this.list(new QueryWrapper<WareSkuEntity>()
                .eq("sku_id", detailEntity.getSkuId()).eq("ware_id", detailEntity.getWareId())
        );
        if(wareSkuEntities != null && wareSkuEntities.size()>0){
            //更新库存
            List<WareSkuEntity> updateWareSku = wareSkuEntities.stream().map( item -> {
                item.setStock(item.getStock()+detailEntity.getSkuNum());
                return item;
            }).collect(Collectors.toList());
            this.updateBatchById(updateWareSku);
        } else {
            //添加库存
            WareSkuEntity wareSkuEntity  = new WareSkuEntity();
            wareSkuEntity.setSkuId(detailEntity.getSkuId());
            // 远程查 sku 的名字，如果失败无需回滚
            try {
                R result = productFeign.info(wareSkuEntity.getSkuId());
                if(result.getCode() != 0){
                    throw new RRException("远程调用失败");
                }
                JSONObject object = JSONObject.from(result.get("skuInfo"));
                wareSkuEntity.setSkuName(object.getString("skuName"));
            } catch (Exception e){
                e.printStackTrace();
            }
            wareSkuEntity.setStock(detailEntity.getSkuNum());
            wareSkuEntity.setWareId(detailEntity.getWareId());
            wareSkuEntity.setStockLocked(0);
            this.save(wareSkuEntity);
        }
    }

    @Override
    public List<SkuHasStockTo> hasStockBySkuIds(List<Long> skuIds) {
        return skuIds.stream().map(skuId -> {
            SkuHasStockTo skuHasStockTo = new SkuHasStockTo();
            skuHasStockTo.setSkuId(skuId);
            Long sumStock = baseMapper.getSumStockBySku(skuId);
            skuHasStockTo.setHasStock(sumStock != null && sumStock > 0);

            return skuHasStockTo;
        }).collect(Collectors.toList());
    }

    @Transactional
    @Override
    public List<LockStockResultTo>  lockStock(WareStockLockTo wareStockLockTo) {
        List<LockStockResultTo> ans = new ArrayList<>();
        Map<Long, Integer> skuIdCountMap = new HashMap<>(); // {key:skuId, value:count}
        List<Long> skuIds = new ArrayList<>();
        Set<Long> skuIdSet = new HashSet<>(); // 剩下的就是没有库存的集合
        List<RLock> wLocks = new ArrayList<>();
        wareStockLockTo.getOrderItems().forEach(item-> {
            skuIdCountMap.put(item.getSkuId(), item.getCount());
            skuIds.add(item.getSkuId());
            skuIdSet.add(item.getSkuId());
            //分布式加锁 key -- STOCK_LOCK_skuId
            String lockKey = "STOCK_LOCK_"+item.getSkuId();
            RReadWriteLock rwLock = redissonClient.getReadWriteLock(lockKey);
            RLock wLock = rwLock.writeLock();
            wLock.lock(90, TimeUnit.SECONDS);
            // 把操作的 skuId 的代码块都锁住
            wLocks.add(wLock);
        });

        try {
            //查询仓储情况
            List<RemainStockResultMap> remainStocks = this.getBaseMapper().queryRemainStockBatch(skuIds);

            // 查购物项中是否有库存 没有库存的情况下，remainStocks.size() != skuIdSet.size()

            //依次锁定库存，若所剩库存不满足要求就将该购物项的 locked 置为 false
            remainStocks.forEach(item -> {
                if (skuIdSet.contains(item.getSkuId())) {
                    // 有就移除，最后判定其长度是否为 0
                    skuIdSet.remove(item.getSkuId());
                }
                LockStockResultTo lockTemp = new LockStockResultTo();
                lockTemp.setSkuId(item.getSkuId());
                boolean flag = true;
                Integer buyCount = skuIdCountMap.get(item.getSkuId());
                //遍历这个sku所在的所有仓库库存请款
                List<LockStockResultTo.WareStockMeta> wareStockMetas = new ArrayList<>();
                for (WareMeta wareMeta : item.getWareMetas()) {
                    if(buyCount <= 0) {
                        // 需求的数量已经锁定库存完毕，跳出循环
                        break;
                    }
                    LockStockResultTo.WareStockMeta wareStockMeta = new LockStockResultTo.WareStockMeta();
                    wareStockMeta.setWareId(wareMeta.getWareId());
                    if(buyCount <= wareMeta.getRemainStock()){
                        //当前仓库就能搞定
                        wareStockMeta.setLockedNnm(buyCount);
                        //更新锁定库存
                        this.baseMapper.updateStockLocked(item.getSkuId(), wareMeta.getWareId(), wareMeta.getStockLocked()+buyCount);
                        buyCount = 0;
                    } else {
                        //跨仓库调货
                        //先把当前库存剩余都锁掉
                        buyCount -= wareMeta.getRemainStock();
                        wareStockMeta.setLockedNnm(wareMeta.getRemainStock());
                        this.baseMapper.updateStockLocked(item.getSkuId(), wareMeta.getWareId(), wareMeta.getStock());
                    }
                    wareStockMetas.add(wareStockMeta);
                }
                lockTemp.setWareStockMetas(wareStockMetas);
                //当减去所有剩余库存还不够所需数量，就设置 locked 为false
                if(buyCount > 0){
                    flag = false;
                }
                lockTemp.setLocked(flag);
                lockTemp.setOrderSn(wareStockLockTo.getOrderSn());
                lockTemp.setOrderId(wareStockLockTo.getOrderId());
                ans.add(lockTemp);
            });

            //没有库存的 skuId 集合
            List<Long> hasNoStockSkuIds = new ArrayList<>(skuIdSet);

            if (!hasNoStockSkuIds.isEmpty()) {
                // 抛出库存不存在异常
                throw new RRException(BizCodeEnum.WARE_NOT_HAS_STOCKS_EXCEPTION, JSON.toJSONString(hasNoStockSkuIds));
            }
            //超出库存的集合
            List<Long> skuIdsOutOfStock = new ArrayList<>();

            //检索购物项的库存情况，搜集锁库存失败的skuId集合
            for (LockStockResultTo item : ans) {
                if (!item.getLocked()){
                    //有商品没锁住
                    skuIdsOutOfStock.add(item.getSkuId());
                }
            }
            if(!skuIdsOutOfStock.isEmpty()){
                //抛出库存不足的异常，并携带 库存不足的skuId集合
                throw new RRException(BizCodeEnum.WARE_OUT_OF_STOCK, JSON.toJSONString(skuIdsOutOfStock));
            }

        } finally {
            for (RLock wLock : wLocks) {
                wLock.unlock();
            }
        }
        return ans;
    }

    @Override
    public void releaseStock(List<WareStockReleaseParam> releaseParams) {
        this.getBaseMapper().releaseStock(releaseParams);
    }
}